package lsystem

/**
 * Created by IntelliJ IDEA.
 * User: koeppelad
 * Date: 4/14/11
 * Time: 8:28 PM
 * To change this template use File | Settings | File Templates.
 */

import scala.util.matching.Regex.Match
import org.lwjgl.opengl.GL11._

class LSystem(private val vars: Set[Variable],
			        private val rules: Set[ProductionRule],
			        private val initState: String)
{ var index: Int = glGenLists(1)
  var maxX = 0f;
  var maxY = 0f;

	private val varRegex =
    ("(" + (for(v <- vars) yield v.name).mkString("|") + ")").r
  private val ruleRegex =
    ("(" + (for(r <- rules) yield r.predecessor).mkString("|") + ")").r

	def iterate(times: Int)
	{
		def iterateHelper(times: Int, state: String): String =
		{
			if(times <= 0)
				return state
			iterateHelper(times - 1, iterateOnce(state))
		}
    glNewList(index, GL_COMPILE)
      glBegin(GL_LINE_STRIP)
		  evaluate(iterateHelper(times, initState))
      glEnd
    glEndList
	}

  def drawIt(){
    glCallList(index);
  }



	private def evaluate(state: String)
	{
		val matchedVars =
		{
			val strs = varRegex.findAllIn(state)
			//If the final get fails, then there is a variable which is used
			//for matching in the regex, but no longer exists in the set vars.
			//This scenario should never occur.
			strs.map{matched => vars.find{_.name.unapplySeq(matched).isDefined}.get}
		}
		matchedVars.foreach{_.action()}
	}

	private def iterateOnce(state: String): String =
	{
		ruleRegex.replaceAllIn(state, replacer _)
	}

	private def replacer(m: Match): String =
	{
		val rule = rules.find{_.predecessor.unapplySeq(m.matched).isDefined}
		if(rule isDefined)
			return rule.get.selectSuccessor
		else
			return m.matched
	}
}

/*object Plant extends Application
{
	override def main(args: Array[String]) =
	{
		val iterations = 6
		val dist = 390 * math.pow(.5, iterations)
		val turn = math.toRadians(25)

		val drawer = new DrawerOld();
		val moveForward = {() => drawer.move(dist, 0, 0)}
		val turnLeft = {() => drawer.move(0, turn, 0)}
		val turnRight = {() => drawer.move(0, -turn, 0)}
		val doNothing = {() =>}
		val push = {() => drawer.push()}
		val pop = {() => drawer.pop()}

		val vars = Set(new Variable("F", moveForward),
					   new Variable("L", turnLeft),
		               new Variable("R", turnRight),
		               new Variable("X", doNothing),
					   new Variable("<", push),
					   new Variable(">", pop))

		val initState = "X"

		val rules = Set(new ProductionRule("F", "FF"),
						new ProductionRule("X", "FR<<X>LX>LF<LFX>RX"))
//		val vars = Set(new Variable("F", moveForward),
//				       new Variable("L", turnLeft),
//				       new Variable("R", turnRight))
//        val initState = "F"
//		val rules = Set(new ProductionRule("F", "FLFRFRFLF"))

		val lsys = new LSystem(vars, rules, initState)
		lsys.iterate(iterations)
		Thread.sleep(10000)
	}
}

object PenroseTilings extends Application
{
	override def main(args: Array[String]) =
	{
		val iterations = 8
		val dist = 1000 * math.pow(.5, iterations)
		val turn = math.Pi / 5

		val drawer = new DrawerOld();
		val moveForward = {() => drawer.move(dist, 0, 0)}
		val turnLeft = {() => drawer.move(0, turn, 0)}
		val turnRight = {() => drawer.move(0, -turn, 0)}
		val doNothing = {() =>}
		val push = {() => drawer.push()}
		val pop = {() => drawer.pop()}

		val vars = Set(new Variable("F", moveForward),
					   new Variable("L", turnLeft),
		               new Variable("R", turnRight),
		               new Variable("W", doNothing),
					   new Variable("X", doNothing),
					   new Variable("Y", doNothing),
					   new Variable("Z", doNothing),
					   new Variable("<", push),
					   new Variable(">", pop))

		val initState = "<X>LL<X>LL<X>LL<X>LL<X>"

		val rules = Set(new ProductionRule("W", "YFLLZFRRRRXF<RYFRRRRWF>LL"),
						new ProductionRule("X", "LYFRRZF<RRRWFRRXF>L"),
						new ProductionRule("Y", "RWFLLXF<LLLYFLLZF>R"),
						new ProductionRule("Z", "RRYFLLLLWF<LZFLLLLXF>RRXF"),
						new ProductionRule("F", ""))

		val lsys = new LSystem(vars, rules, initState)
		lsys.iterate(iterations)
		Thread.sleep(10000)
	}
}

object KochCurve extends Application
{
	override def main(args: Array[String]) =
	{
		val iterations = 6
		val dist = 60//500 * math.pow(.5, iterations)
		val turn = math.Pi / 2

		val drawer = new DrawerOld();
		val moveForward = {() => drawer.move(dist, 0, 0)}
		val turnLeft = {() => drawer.move(0, turn, 0)}
		val turnRight = {() => drawer.move(0, -turn, 0)}

		val vars = Set(new Variable("F", moveForward),
				       new Variable("L", turnLeft),
				       new Variable("R", turnRight))
        val initState = "F"
		val rules = Set(new ProductionRule("F", "FLFRFRFLF"))

		val lsys = new LSystem(vars, rules, initState)
		lsys.iterate(iterations)
		Thread.sleep(10000)
	}
}

object KochCurve2 extends Application
{
  override def main(args: Array[String])
  {
    val iterations = 5
    val dist = 500 * math.pow(.5, iterations)
    val turn = math.Pi / 2

    val drawer = new DrawerOld();
    val moveForward = {() => drawer.move(dist, 0, 0)}
    val turnLeft = {() => drawer.move(0, turn, 0)}
    val turnRight = {() => drawer.move(0, -turn, 0)}
    val doNothing = {() =>}
    val push = {() => drawer.push()}
    val pop = {() => drawer.pop()}

    val vars = Set(new Variable("F", moveForward),
                   new Variable("L", turnLeft),
                   new Variable("R", turnRight))

    val initState = "FRFRFRF"

    val rules = Set(new ProductionRule("F", "FFRFRFRFRFRFLF"))

    val lsys = new LSystem(vars, rules, initState)
    lsys.iterate(iterations)
    Thread.sleep(10000)
  }
}  */